/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/**
 * All devices are abstracted as device structures, such as AT24C02, W25Q64 and so on, 
 * and the peripherals of the chip are also abstracted as device structures. if you want to add a new device, 
 * you should use device register interface, and if you want to delete a device, you should unregister it first.
 * when you register a device, the device-name directory will be created in the '/xxx_bus/device/' directory.
 * 
 * for example:
 * the device name is 'i2c-eeprom', the device-name directory will be created in the '/i2c/device/i2c-eeprom' directory.
 * the device name is 'spi-flash', the device-name directory will be created in the '/spi/device/spi-flash' directory.
 */


#include <kernel/bus.h>
#include <kernel/device.h>
#include <kernel/kobject.h>
#include <kernel/syslog.h>
#include <string.h>
#include <list.h>


/**
 * @brief check whether the device is registered
 * @param dev device pointer
 * @return true or false, true: registered, false: unregistered
 */
bool device_is_registered(struct device *dev)
{
    if(kobj_find_by_name(&dev->bus->kobj, dev->name) == NULL) {
        return false;
    }
    else {
        return true;
    }
}


/**
 * @brief get device name
 * @param dev device pointer
 * @return device name
 */
const char *device_get_name(struct device *dev)
{
    if(dev && dev->kobj.name) {
        return dev->kobj.name;
    }

    return NULL;
}


/**
 * @brief set device name
 * @param dev device pointer
 * @param name device name
 * @return none
 */
void device_set_name(struct device *dev, const char *name)
{
    dev->name = name;
    dev->kobj.name = name;
}


/**
 * @brief match device with driver
 * @param dev device pointer
 * @return none
 */
static void device_match_driver(struct device* dev)
{
    struct bus_type *bus = dev->bus;
    struct driver *find_drv = NULL;
    kobj_t *kobj_node;
    struct list_node *head = &bus->drv_kobj.child;

    list_for_each_entry(kobj_node, head, kobj_t, entry) {
        
        find_drv = container_of(kobj_node, struct driver, kobj);

        if(bus->match(dev, find_drv) == 0) {
            dev->driver = find_drv;
            device_create_text_attr(dev, "bound", (char*)find_drv->name);
            if(dev->bus->probe) {
                dev->bus->probe(dev);
            }
            return;
        }
    }
}


/**
 * @brief register device
 * @param dev device pointer
 * @return int, 0: success, -1: failed
 */
int device_register(struct device *dev)
{
    if(dev == NULL) {
        return -1;
    }

    if(dev->name == NULL) {
        SYSLOG_ERROR("device's name is not set");
        return -1;
    }

    if(dev->bus == NULL) {
        SYSLOG_ERROR("device's bus is not set");
        return -1;
    }

    if(device_is_registered(dev)) {
        SYSLOG_ERROR("the device is registered");
        return -1;
    }

    kobj_init(&dev->kobj, dev->name, NULL);

    if(!kobj_add(&dev->bus->dev_kobj, &dev->kobj)){
        return -1;
    }

    /* foreach all bus driver, if match, to do init driver pointer */
    device_match_driver(dev);

    return 0;
}


/**
 * @brief unregister device
 * @param dev device pointer
 * @return none
 */
void device_unregister(struct device *dev)
{
    if(device_is_registered(dev)) {
        kobj_remove_self(&dev->kobj);
        return;
    }

    SYSLOG_WARN("device is not register, so it can not delete");
}
